﻿Imports System.IO
Imports DiscUtils
Imports Dokan

''' <summary>
''' A bridge that adapts any DiscUtils filesystem to dokan
''' </summary>
Public Class bridge
    Implements Dokan.DokanOperations
    Dim fsData As Stream
    'Dim discImg As New DiscUtils.Vdi.DiskImageFile(fsData)
    Public disc As Vhd.Disk
    Public p1s
    Public p1 As DiscFileSystem
    'Dim disc As New Fat.FatFileSystem(fsData)
    Public log As Action(Of String) = Sub(txt As String)
                                          Debug.WriteLine(txt)
                                      End Sub
    Private IsMounted As Boolean = False
    Public Sub Mount(ByVal mountpoint As String, Optional ByVal NetDrive As Boolean = False, Optional ByVal RemovableDrive As Boolean = False)
        If IsMounted Then Exit Sub
        Dim fs As DiscFileSystem = Me.p1
        Dim opt As New DokanOptions
        opt.MountPoint = mountpoint
        opt.DebugMode = True
        opt.UseStdErr = False
        opt.VolumeLabel = fs.VolumeLabel
        opt.NetworkDrive = NetDrive
        opt.RemovableDrive = RemovableDrive
        opt.ThreadCount = 1
        log = Sub(txt As String)
                  Console.WriteLine(txt)
              End Sub
        ' Shell(opt.MountPoint)
        Dim status As Integer = DokanNet.DokanMain(opt, Me)
        Select Case status
            Case DokanNet.DOKAN_DRIVE_LETTER_ERROR
                Console.WriteLine("Drvie letter error")
                Exit Select
            Case DokanNet.DOKAN_DRIVER_INSTALL_ERROR
                Console.WriteLine("Driver install error")
                Exit Select
            Case DokanNet.DOKAN_MOUNT_ERROR
                Console.WriteLine("Mount error")
                Exit Select
            Case DokanNet.DOKAN_START_ERROR
                Console.WriteLine("Start error")
                Exit Select
            Case DokanNet.DOKAN_ERROR
                Console.WriteLine("Unknown error")
                Exit Select
            Case DokanNet.DOKAN_SUCCESS
                Console.WriteLine("Success")
                Exit Select
            Case Else
                Console.WriteLine("Unknown status: " & status)
                Exit Select
        End Select
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the <see cref="bridge" /> class.
    ''' </summary>
    ''' <param name="fs">The DiscFileSystem to mount.</param>
    Public Sub New(ByVal fs As DiscFileSystem)
        p1 = fs 'New Ntfs.NtfsFileSystem(p1s)
    End Sub

    ''' <summary>
    ''' Cleanups the specified filename.
    ''' </summary>
    ''' <param name="filename">The filename.</param>
    ''' <param name="info">The info.</param><returns></returns>
    Public Function Cleanup(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.Cleanup
        log("CLEANUP " & filename)
        Return 0
    End Function

    ''' <summary>
    ''' Closes the file.
    ''' </summary>
    ''' <param name="filename">The filename.</param>
    ''' <param name="info">The info.</param><returns></returns>
    Public Function CloseFile(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.CloseFile
        log("CLOSEFILE " & filename)
        Return 0
    End Function

    Public Function CreateDirectory(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.CreateDirectory
        log("CREATEDIR " & filename)
        SyncLock p1
            p1.CreateDirectory(filename)
        End SyncLock
        info.IsDirectory = True
        Return 0
    End Function

    Public Function CreateFile(ByVal filename As String, ByVal access As System.IO.FileAccess, ByVal share As System.IO.FileShare, ByVal mode As System.IO.FileMode, ByVal options As System.IO.FileOptions, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.CreateFile
        log("CREATEFILE " & filename)
        SyncLock p1
            Try
                If p1.DirectoryExists(filename) Then info.IsDirectory = True
                If Not p1.FileExists(filename) Then
                    With p1.OpenFile(filename, FileMode.OpenOrCreate, access)
                        .Write(New Byte() {0}, 0, 0)
                        .Flush()
                        .Close()
                    End With
                End If
            Catch ex As Exception

            End Try
        End SyncLock
        Return 0
    End Function

    Public Function DeleteDirectory(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.DeleteDirectory
        log("DELETEDIR " & filename)
        SyncLock p1
            p1.DeleteDirectory(filename, True)
        End SyncLock
        Return 0
    End Function

    Public Function DeleteFile(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.DeleteFile
        log("DELETEFILE " & filename)
        SyncLock p1
            p1.DeleteFile(filename)
        End SyncLock
        Return 0
    End Function

    Public Function FindFiles(ByVal filename As String, ByVal files As System.Collections.ArrayList, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.FindFiles
        log("FINDFILES " & filename)
        Dim fd As New List(Of String)
        SyncLock p1
            fd.AddRange(p1.GetFiles(filename))
            For Each f In fd
                Dim fileinfo As New FileInformation
                If p1.FileExists(f) Then
                    Dim fi = p1.GetFileInfo(f)
                    fileinfo.FileName = fi.Name
                    ' finfo.Attributes =    'System.IO.FileAttributes.Normal
                    fileinfo.Attributes = fi.Attributes
                    fileinfo.LastAccessTime = fi.LastAccessTime
                    fileinfo.LastWriteTime = fi.LastWriteTime
                    fileinfo.CreationTime = fi.CreationTime
                    files.Add(fileinfo)
                End If
            Next
            fd.Clear()
            fd.AddRange(p1.GetDirectories(filename))
            For Each f In fd
                Dim fileinfo As New FileInformation
                If p1.DirectoryExists(f) Then
                    Dim fi = p1.GetDirectoryInfo(f)
                    fileinfo.FileName = fi.Name
                    ' finfo.Attributes =    'System.IO.FileAttributes.Normal
                    fileinfo.Attributes = fi.Attributes
                    fileinfo.LastAccessTime = fi.LastAccessTime
                    fileinfo.LastWriteTime = fi.LastWriteTime
                    fileinfo.CreationTime = fi.CreationTime
                    files.Add(fileinfo)
                End If
            Next
        End SyncLock
        Return 0
    End Function

    Public Function FlushFileBuffers(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.FlushFileBuffers
        log("FLUSHFILEBUFFERS " & filename)
        Return 1
    End Function

    Public Function GetDiskFreeSpace(ByRef freeBytesAvailable As ULong, ByRef totalBytes As ULong, ByRef totalFreeBytes As ULong, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.GetDiskFreeSpace
        log("GETFREESPACE")
        freeBytesAvailable = 100 * 1024 * 1024
        totalBytes = 100 * 1024 * 1024
        totalFreeBytes = 100 * 1024 * 1024
        Return 0
    End Function

    Public Function GetFileInformation(ByVal filename As String, ByVal fileinfo As Dokan.FileInformation, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.GetFileInformation
        log("GETFILEINFO " & filename)
        Dim fi As DiscFileInfo
        Dim di As DiscDirectoryInfo
        Dim ei = Nothing
        If p1.DirectoryExists(filename) Then info.IsDirectory = True
        If info.IsDirectory Then
            SyncLock p1
                If p1.DirectoryExists(filename) Then
                    di = p1.GetDirectoryInfo(filename)
                    ei = di
                End If
            End SyncLock
        Else
            SyncLock p1
                If p1.FileExists(filename) Then
                    fi = p1.GetFileInfo(filename)
                    fileinfo.Length = fi.Length
                    ei = fi
                End If
            End SyncLock
        End If
        If ei Is Nothing Then Return -1
        Dim ex As Boolean
        SyncLock p1
            ex = p1.Exists(filename)
        End SyncLock
        If ex Then
            fileinfo.FileName = ei.Name
            ' finfo.Attributes =    'System.IO.FileAttributes.Normal
            If ei.Attributes IsNot Nothing Then fileinfo.Attributes = ei.Attributes
            If ei.LastAccessTime IsNot Nothing Then fileinfo.LastAccessTime = ei.LastAccessTime
            If ei.LastWriteTime IsNot Nothing Then fileinfo.LastWriteTime = ei.LastWriteTime
            If ei.CreationTime IsNot Nothing Then fileinfo.CreationTime = ei.CreationTime
            Return 0
        Else
            Return -1 * Dokan.DokanNet.ERROR_FILE_NOT_FOUND
        End If

    End Function

    Public Function LockFile(ByVal filename As String, ByVal offset As Long, ByVal length As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.LockFile
        log("LOCKFILE " & filename)
        Return 0
    End Function

    Public Function MoveFile(ByVal filename As String, ByVal newname As String, ByVal replace As Boolean, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.MoveFile
        log("MOVEFILE " & filename & " NEW: " & newname)
        SyncLock p1
            If p1.DirectoryExists(filename) Then
                info.IsDirectory = True
                p1.MoveDirectory(filename, newname)
            ElseIf p1.FileExists(filename) Then
                info.IsDirectory = False
                p1.MoveFile(filename, newname, replace)
            End If
        End SyncLock
        Return 0
    End Function

    Public Function OpenDirectory(ByVal filename As String, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.OpenDirectory
        log("OPENDIR " & filename)
        Return 0
    End Function

    Public Function ReadFile(ByVal filename As String, ByVal buffer() As Byte, ByRef readBytes As UInteger, ByVal offset As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.ReadFile
        log("READFILE " & filename)
        Dim fs As Stream
        Try
            SyncLock p1
                fs = p1.OpenFile(filename, FileMode.Open, FileAccess.Read)
                fs.Seek(offset, SeekOrigin.Begin)
                readBytes = CUInt(fs.Read(buffer, 0, buffer.Length))
                fs.Close()
            End SyncLock
            Return 0
        Catch generatedExceptionName As Exception
            Debug.Write(String.Format("READFILE: {0} {1} {2}", filename, generatedExceptionName.Message, generatedExceptionName.StackTrace))
            Return -1
        End Try
    End Function

    Public Function SetAllocationSize(ByVal filename As String, ByVal length As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.SetAllocationSize
        log("SETALLOCSIZE " & filename)
        Return 0
    End Function

    Public Function SetEndOfFile(ByVal filename As String, ByVal length As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.SetEndOfFile
        log("SETEOF " & filename)
        Return 0
    End Function

    Public Function SetFileAttributes(ByVal filename As String, ByVal attr As System.IO.FileAttributes, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.SetFileAttributes
        log("SETFILEATTRIB " & filename)
        Return 0
    End Function

    Public Function SetFileTime(ByVal filename As String, ByVal ctime As Date, ByVal atime As Date, ByVal mtime As Date, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.SetFileTime
        log("SETFILETIME " & filename)
        Return 0
    End Function

    Public Function UnlockFile(ByVal filename As String, ByVal offset As Long, ByVal length As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.UnlockFile
        log("UNLOCKFILE " & filename)
        Return 0
    End Function

    Public Function Unmount(ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.Unmount
        log("UNMOUNT")
        Return 0
    End Function

    Public Function WriteFile(ByVal filename As String, ByVal buffer() As Byte, ByRef writtenBytes As UInteger, ByVal offset As Long, ByVal info As Dokan.DokanFileInfo) As Integer Implements Dokan.DokanOperations.WriteFile
        log("WRITEFILE " & filename & " offset=" & offset & " buffer.Length=" & buffer.Length)
        Try
            SyncLock (p1)

                Using fs As Stream = p1.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite)
                    If Not fs.CanWrite Then Return -DokanNet.ERROR_ACCESS_DENIED
                    fs.Seek(offset, SeekOrigin.Begin)
                    fs.Write(buffer, 0, buffer.Length)
                    writtenBytes = buffer.Length
                    fs.Flush()
                End Using

                Return 0
            End SyncLock

        Catch generatedExceptionName As Exception
            Debug.Write(String.Format("WRITEFILE: {0} {1} {2}", filename, generatedExceptionName.Message, generatedExceptionName.StackTrace))
            Return 0
        End Try
    End Function
End Class

Public Class Hmmmm

End Class
